package br.com.educa.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.MappedSuperclass;
import javax.persistence.Query;



@MappedSuperclass
@SuppressWarnings({"rawtypes"})
public class ReflectionUtils{
	
	Class clazz;

	 private static final HashSet<Class<?>> WRAPPER_TYPES = getWrapperTypes();

    private static HashSet<Class<?>> getWrapperTypes()
    {
        HashSet<Class<?>> ret = new HashSet<Class<?>>();
        ret.add(String.class);
        ret.add(Boolean.class);
        ret.add(Character.class);
        ret.add(Byte.class);
        ret.add(Short.class);
        ret.add(Integer.class);
        ret.add(Long.class);
        ret.add(Float.class);
        ret.add(Double.class);
        ret.add(Void.class);
        return ret;
    }
	
	public ReflectionUtils(String className){
		try {
			this.clazz = Class.forName(className) ;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static boolean isWrapperType(Class<?> clazz)
    {
        return WRAPPER_TYPES.contains(clazz);
    }
	
	public EntityManager getEntityManager(){
		EntityManager em =null;
		try {
			Object instance = clazz.newInstance();
			Method m[] = clazz.getDeclaredMethods(); 
			for (Method method : m) {
				if(method.getName().equals("entityManager")){
					em =(EntityManager) method.invoke(instance,new Object[0]);
					break;
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return em;
	}

	public Class getClazz() {
		return clazz;
	}
	
	public  List getResultList(String sql) {
		EntityManager em = getEntityManager();
		Query q = em.createNativeQuery(sql, clazz);
		return q.getResultList();
	}
	
	public Long getSingleResultAsLong(String sql) {
		EntityManager em = getEntityManager();
		Query q = em.createNativeQuery(sql);
		return ((BigInteger) q.getSingleResult()).longValue();
	}
	
	public Method getMethodByName(String methodName){
		Method retorno = null;
		try {
			Object instance = clazz.newInstance();
			Method m[] = clazz.getDeclaredMethods();
			for (Method method : m) {
				if(method.getName().equals(methodName)){
					retorno = method;
				}
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return retorno;
	}
	
	public String transformInJson(Object result){
		Method toJson = getMethodByName("toJson");
		String retorno = null;
		try {
			retorno =(String) toJson.invoke(result,new Object[0]);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return retorno;
	}
	
	public String transformInJsonArray(Object result){
		Method toJson = getMethodByName("toJsonArray");
		String retorno = null;
		try {
			retorno =(String) toJson.invoke(result,new Object[0]);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return retorno;
	}
	
	public Object find(String methodName, Object[] args){
		Method finder = getMethodByName(methodName);
		Object result = null;
		try {
			Object instance = clazz.newInstance();
			result =finder.invoke(instance,args);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public Object findById(Long id){
		
		Method finder = getMethodByName("find"+clazz.getSimpleName());
		Object result = null;
		Object[] args = new Object[1];
		args[0] =id;
		try {
			Object instance = clazz.newInstance();
			result =finder.invoke(instance,args);
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public List findAll(){
		List result = null;
		Method m[] = clazz.getDeclaredMethods();
		for (Method method : m) {
			if(method.getName().startsWith("findAll") && !method.getName().contains("_around")){
				try{
					Object instance = clazz.newInstance();
					result=(List) method.invoke(instance,new Object[0]);
					break;
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (InstantiationException e) {
					e.printStackTrace();
				}
			}
		}
		return result;
	}
	
	
}
